Alpha Threshold¶
NiiVue allows asymmetric positive / negative statistical thresholds. It can also make sub-threshold voxels translucent.
See https://niivue.com/demos/features/alphathreshold.html for mirror.
In [1]:
from pathlib import Path
from ipyniivue import download_dataset
BASE_API_URL = "https://niivue.com/demos/images/"
DATA_FOLDER = Path("images")
# Download data for example
download_dataset(
BASE_API_URL,
DATA_FOLDER,
files=[
"fslmean.nii.gz",
"fslt.nii.gz",
],
)
fslmean.nii.gz already exists. fslt.nii.gz already exists. Dataset downloaded successfully to images.
In [2]:
## Import Required Libraries
from pathlib import Path
import ipywidgets as widgets
from IPython.display import display
from ipyniivue import NiiVue, ShowRender, SliceType
DATA_FOLDER = Path("images")
## Create NiiVue Instance and Load Data
# Create NiiVue instance with specific settings
v = NiiVue(
loading_text="there are no images",
back_color=(1, 1, 1, 1),
show_3d_crosshair=True,
is_colorbar=True,
multiplanar_show_render=ShowRender.ALWAYS,
)
# Set initial configuration
v.set_radiological_convention(False)
v.set_slice_type(SliceType.MULTIPLANAR)
v.set_slice_mm(False)
v.set_interpolation(True)
# Load volumes
v.load_volumes(
[
{"path": DATA_FOLDER / "fslmean.nii.gz"},
{
"path": DATA_FOLDER / "fslt.nii.gz",
"colormap": "warm",
"colormap_negative": "winter",
"cal_min": 3,
"cal_max": 6,
"cal_min_neg": -6,
"cal_max_neg": -3,
},
]
)
# Hide colorbar for anatomical scan
v.volumes[0].colorbar_visible = False
# Set initial overlay outline
v.overlay_outline_width = 0.25
## Create Interactive Controls
# High DPI checkbox
dpi_checkbox = widgets.Checkbox(
value=True,
description="High DPI",
tooltip="Higher resolution for 'retina' displays",
)
# Negative colors checkbox
negative_checkbox = widgets.Checkbox(value=True, description="Negative Colors")
# Smooth checkbox
smooth_checkbox = widgets.Checkbox(
value=False,
description="Smooth",
tooltip=(
"Trilinear interpolation blurs data, "
"but can change which voxels survive a threshold"
),
)
# World space checkbox
world_checkbox = widgets.Checkbox(value=False, description="World Space")
# Threshold sliders
neg_thresh_slider = widgets.IntSlider(
min=1, max=50, value=30, description="-Thresh", continuous_update=True
)
neg_max_slider = widgets.IntSlider(
min=51, max=150, value=60, description="-Max", continuous_update=True
)
pos_thresh_slider = widgets.IntSlider(
min=1, max=50, value=1, description="+Thresh", continuous_update=True
)
pos_max_slider = widgets.IntSlider(
min=51, max=150, value=60, description="+Max", continuous_update=True
)
# Outline width slider
outline_slider = widgets.IntSlider(
min=0, max=4, value=1, description="Outline", continuous_update=True
)
# Alpha mode dropdown
alpha_dropdown = widgets.Dropdown(
options=[
("Restrict colorbar to range", 0),
("Colorbar from 0, transparent subthreshold", 1),
("Colorbar from 0, translucent subthreshold", 2),
],
value=0,
description="Alpha Mode:",
)
# Location display
location_output = widgets.HTML(value=" ")
## Setup Event Handlers
def on_dpi_change(change):
"""Handle DPI checkbox changes."""
v.set_high_resolution_capable(change["new"])
def on_negative_change(change):
"""Handle negative colormap checkbox changes."""
if change["new"]:
v.set_colormap_negative(v.volumes[1].id, "winter")
else:
v.set_colormap_negative(v.volumes[1].id, "")
def on_smooth_change(change):
"""Handle smooth interpolation checkbox changes."""
v.set_interpolation(not change["new"])
def on_world_change(change):
"""Handle world space checkbox changes."""
v.set_slice_mm(change["new"])
def on_pos_thresh_change(change):
"""Handle positive threshold slider changes."""
v.volumes[1].cal_min = 0.1 * change["new"]
def on_neg_max_change(change):
"""Handle negative maximum slider changes."""
v.volumes[1].cal_min_neg = -0.1 * change["new"]
def on_pos_max_change(change):
"""Handle positive maximum slider changes."""
v.volumes[1].cal_max = 0.1 * change["new"]
def on_neg_thresh_change(change):
"""Handle negative threshold slider changes."""
v.volumes[1].cal_max_neg = -0.1 * change["new"]
def on_outline_change(change):
"""Handle outline width slider changes."""
v.overlay_outline_width = 0.25 * change["new"]
def on_alpha_mode_change(change):
"""Handle alpha mode dropdown changes."""
v.volumes[1].colormap_type = change["new"]
@v.on_location_change
def handle_location_change(data):
"""Update location display when crosshair moves."""
location_output.value = f" {data['string']}"
# Attach event handlers
dpi_checkbox.observe(on_dpi_change, names="value")
negative_checkbox.observe(on_negative_change, names="value")
smooth_checkbox.observe(on_smooth_change, names="value")
world_checkbox.observe(on_world_change, names="value")
pos_thresh_slider.observe(on_pos_thresh_change, names="value")
neg_max_slider.observe(on_neg_max_change, names="value")
pos_max_slider.observe(on_pos_max_change, names="value")
neg_thresh_slider.observe(on_neg_thresh_change, names="value")
outline_slider.observe(on_outline_change, names="value")
alpha_dropdown.observe(on_alpha_mode_change, names="value")
# Initialize values
on_pos_thresh_change({"new": pos_thresh_slider.value})
on_pos_max_change({"new": pos_max_slider.value})
on_neg_max_change({"new": neg_max_slider.value})
on_neg_thresh_change({"new": neg_thresh_slider.value})
on_outline_change({"new": outline_slider.value})
on_alpha_mode_change({"new": alpha_dropdown.value})
## Display All
# Organize controls
controls_row1 = widgets.HBox(
[dpi_checkbox, negative_checkbox, smooth_checkbox, world_checkbox]
)
controls_row2 = widgets.HBox([neg_thresh_slider, neg_max_slider])
controls_row3 = widgets.HBox([pos_thresh_slider, pos_max_slider])
controls_row4 = widgets.HBox([outline_slider, alpha_dropdown])
# Create main layout
controls = widgets.VBox(
[controls_row1, controls_row2, controls_row3, controls_row4, location_output]
)
# Display everything
display(widgets.VBox([controls, v]))